using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Grammars.Core
{
    public class ProductionList<T> : Rule<T>, IEnumerable<ProductionRule<T>> 
        where T : Node, new()
    {
        internal List<ProductionRule<T>> List;

        internal ProductionList()
        {
            List = new List<ProductionRule<T>>();
        }

        public static Def<T> operator %(Def<T> left, ProductionList<T> right)
        {
            left.List = right;
            right.Grammar = left.Grammar;
            left.Grammar.Productions.Add(new Production<T>(left, right));
            return left;
        }

        public static ProductionList<T> operator |(Def<T> left, ProductionList<T> right)
        {
            var rule = new ProductionRule<T>();
            rule.Grammar = left.Grammar;
            rule.Defs.Add(left);

            foreach (var sr in left.SemanticRules)
                rule.AddRule(sr);

            right.List.Insert(0, rule);
            right.Grammar = left.Grammar;
            return right;
        }

        public static ProductionList<T> operator |(ProductionList<T> left, Def<T> right)
        {
            var rule = new ProductionRule<T>();
            rule.Grammar = right.Grammar;
            rule.Defs.Add(right);

            foreach (var sr in right.SemanticRules)
                rule.AddRule(sr);

            left.List.Add(rule);
            left.Grammar = right.Grammar;
            return left;
        }

        public static ProductionList<T> operator |(ProductionRule<T> left, ProductionList<T> right)
        {
            right.List.Insert(0, left);
            return right;
        }

        public static ProductionList<T> operator |(ProductionList<T> left, ProductionRule<T> right)
        {
            left.List.Add(right);
            return left;
        }

        public static ProductionList<T> operator |(ProductionList<T> left, ProductionList<T> right)
        {
            left.List.AddRange(right.List);
            return left;
        }

        public IEnumerator<ProductionRule<T>> GetEnumerator()
        {
            return List.GetEnumerator();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(List[0]);

            for (int i = 1; i < List.Count; i++)
                sb.Append(" | " + List[i]);

            return sb.ToString();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}